<?php

	/**
	使用说明：
	$zip = new CZip("F:/work/pic.zip","af/");
	$zip->unzip();
	**/
class CzipComponent extends Object{
	var $savePath; //解包存放的路径,为绝对路径
	var $zipFullPath; //ZIP包的完整路径
	var $srcPath; //zip包所在的路径，必须为绝对路径
	var $zipName; //zip包名
	var $errCode; //错误信息
	var $zipFile = array (); //zip的文件名
	function Czip($zip, $savePath = "./") {
		$this->savePath = $savePath;
		//获得ZIP包名
		if (preg_match("/([[:alpha:]]:[\/\w]*)\/(\w*.zip$)/i", $zip, $match)) {
			$this->srcPath = $match[1] . "/";
			$this->zipName = $match[2];
			$this->zipFullPath = $zip;
			return true;
		} else {
			$this->errCode = "invalid file";
			return false;
		}

	}
	/***
	  函数名： pMakeDir
	  函数功能：建立路径
	  函数参数：@strDir 为要建立的路径。 如建多级目录，目录以'/' 隔开
	**/
	function pMakeDir($strDir, $srcDir = "./") {
		//如果发现有'/',就分出来，建立路径
		preg_match_all('/\w*\/{1}/', $strDir, $match);
		$strMatch = $srcDir;
		//print_r($match);
		for ($i = 0; $i < sizeof($match[0]); $i++) {
			$strMatch .= $match[0][$i];
			if (!file_exists($strMatch)) {
				@ mkdir($strMatch);
				@ chmod(0777);
			}
		}
		return $strMatch;
	}

	//解压缩文件
	function unzip() {
		if (file_exists($this->zipFullPath)) {
			if (!file_exists($this->savePath))
				$this->pMakeDir($this->savePath, "");
			$zip = zip_open($this->zipFullPath);
			if ($zip) {
				while ($zip_entry = zip_read($zip)) {
					//获得ZIP下的文件或路录名
					$name = zip_entry_name($zip_entry);
					//建立文件名中包含的路径
					$curDir = $this->pMakeDir($name, $this->savePath);
					//打开ZIP包，以只读的方式
					if (zip_entry_open($zip, $zip_entry, "r")) {
						//读取ZIP包中的内容
						$buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
						//如果是文件，则建立新文件
						if (!preg_match("/\/$/", $name)) {
							$handle = fopen($this->savePath . $name, 'a+');
							fwrite($handle, $buf);
							fclose($handle);
							zip_entry_close($zip_entry);
							$this->zipFile[] = $name;
						}
					}
				}
				zip_close($zip);
			} else {
				$this->errCode = "无效的ZIP包";
				return false;
			}
		}
		return true;
	}

	/***
	  函数名： delZip
	  函数功能：删除ZIP包
	**/
	function delZip() {
		if (file_exists($this->zipFullPath)) {
			unlink($this->zipFullPath);
		}
	}
	
	
	/////////////////////////////////////////////////////
	/////////////////////////////////////////////////////
	
	
//	function Zip($dir, $zipfilename)
//	{
//		if (@function_exists('gzcompress'))
//		{ 
//			$curdir = getcwd();
//			if (is_array($dir)) 
//			{
//				$filelist = $dir;
//			}
//			else 
//			{
//				$filelist = $this -> GetFileList($dir);
//			}
//			
//			if ((!empty($dir))&&(!is_array($dir))&&(file_exists($dir))) chdir($dir);
//			else chdir($curdir);
//			
//			if (count($filelist)>0)
//			{
//				foreach($filelist as $filename)
//				{
//					if (is_file($filename))
//					{
//						$fd = fopen ($filename, "r");
//						$content = fread ($fd, filesize ($filename));
//						fclose ($fd);
//						
//						if (is_array($dir)) $filename = basename($filename);
//						$this -> addFile($content, $filename);
//					}
//				}
//				$out = $this -> file();
//				
//				chdir($curdir);
//				$fp = fopen($zipfilename, "w");
//				fwrite($fp, $out, strlen($out));
//				fclose($fp);
//			}
//			return 1;
//		} 
//		else return 0;
//	}
//
//	function GetFileList($dir)
//	{
//		if (file_exists($dir))
//		{
//			$args = func_get_args();
//			
//			
//			$pref = $args[1];
//						
//			$dh = opendir($dir);
//			while($files = readdir($dh))
//			{
//				if (($files!=".")&&($files!="..")) 
//				{
//					if (is_dir($dir.$files)) 
//					{
//						$curdir = getcwd();
//						chdir($dir.$files);
//						$file = array_merge($file, $this -> GetFileList("$files", "$pref$files/"));
//						chdir($curdir);
//					}
//					else $file[]=$pref.$files;
//				}
//			}
//			closedir($dh);
//		}
//		return $file;
//		pr($file);
//	}
//
//var $datasec = array();
//var $ctrl_dir = array();
//var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
//var $old_offset = 0;
//
///**
//* Converts an Unix timestamp to a four byte DOS date and time format (date
//* in high two bytes, time in low two bytes allowing magnitude comparison).
//*
//* @param integer the current Unix timestamp
//*
//* @return integer the current date in a four byte DOS format
//*
//* @access private
//*/
//	function unix2DosTime($unixtime = 0) {
//		$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
//		
//		if ($timearray['year'] < 1980) {
//			$timearray['year'] = 1980;
//			$timearray['mon'] = 1;
//			$timearray['mday'] = 1;
//			$timearray['hours'] = 0;
//			$timearray['minutes'] = 0;
//			$timearray['seconds'] = 0;
//		} // end if
//		
//		return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
//		($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
//	} // end of the 'unix2DosTime()' method
//
//
///**
//* Adds "file" to archive
//*
//* @param string file contents
//* @param string name of the file in the archive (may contains the path)
//* @param integer the current timestamp
//*
//* @access public
//*/
//	function addFile($data, $name, $time = 0)
//	{
//		$name = str_replace('\\', '/', $name);
//		
//		$dtime = dechex($this->unix2DosTime($time));
//		$hexdtime = '\x' . $dtime[6] . $dtime[7]
//		. '\x' . $dtime[4] . $dtime[5]
//		. '\x' . $dtime[2] . $dtime[3]
//		. '\x' . $dtime[0] . $dtime[1];
//		eval('$hexdtime = "' . $hexdtime . '";');
//		
//		$fr = "\x50\x4b\x03\x04";
//		$fr .= "\x14\x00"; // ver needed to extract
//		$fr .= "\x00\x00"; // gen purpose bit flag
//		$fr .= "\x08\x00"; // compression method
//		$fr .= $hexdtime; // last mod time and date
//		
//		// "local file header" segment
//		$unc_len = strlen($data);
//		$crc = crc32($data);
//		$zdata = gzcompress($data);
//		$c_len = strlen($zdata);
//		$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
//		$fr .= pack('V', $crc); // crc32
//		$fr .= pack('V', $c_len); // compressed filesize
//		$fr .= pack('V', $unc_len); // uncompressed filesize
//		$fr .= pack('v', strlen($name)); // length of filename
//		$fr .= pack('v', 0); // extra field length
//		$fr .= $name;
//		
//		// "file data" segment
//		$fr .= $zdata;
//		
//		// "data descriptor" segment (optional but necessary if archive is not
//		// served as file)
//		$fr .= pack('V', $crc); // crc32
//		$fr .= pack('V', $c_len); // compressed filesize
//		$fr .= pack('V', $unc_len); // uncompressed filesize
//		
//		// add this entry to array
//		$this -> datasec[] = $fr;
//		$new_offset = strlen(implode('', $this->datasec));
//		
//		// now add to central directory record
//		$cdrec = "\x50\x4b\x01\x02";
//		$cdrec .= "\x00\x00"; // version made by
//		$cdrec .= "\x14\x00"; // version needed to extract
//		$cdrec .= "\x00\x00"; // gen purpose bit flag
//		$cdrec .= "\x08\x00"; // compression method
//		$cdrec .= $hexdtime; // last mod time & date
//		$cdrec .= pack('V', $crc); // crc32
//		$cdrec .= pack('V', $c_len); // compressed filesize
//		$cdrec .= pack('V', $unc_len); // uncompressed filesize
//		$cdrec .= pack('v', strlen($name) ); // length of filename
//		$cdrec .= pack('v', 0 ); // extra field length
//		$cdrec .= pack('v', 0 ); // file comment length
//		$cdrec .= pack('v', 0 ); // disk number start
//		$cdrec .= pack('v', 0 ); // internal file attributes
//		$cdrec .= pack('V', 32 ); // external file attributes - 'archive' bit set
//		
//		$cdrec .= pack('V', $this -> old_offset ); // relative offset of local header
//		$this -> old_offset = $new_offset;
//		
//		$cdrec .= $name;
//		
//		// optional extra field, file comment goes here
//		// save to central directory
//		$this -> ctrl_dir[] = $cdrec;
//	} // end of the 'addFile()' method
//
//
///**
//* Dumps out file
//*
//* @return string the zipped file
//*
//* @access public
//*/
//	function file()
//	{
//		$data = implode('', $this -> datasec);
//		$ctrldir = implode('', $this -> ctrl_dir);
//		
//		return
//		$data .
//		$ctrldir .
//		$this -> eof_ctrl_dir .
//		pack('v', sizeof($this -> ctrl_dir)) . // total # of entries "on this disk"
//		pack('v', sizeof($this -> ctrl_dir)) . // total # of entries overall
//		pack('V', strlen($ctrldir)) . // size of central dir
//		pack('V', strlen($data)) . // offset to start of central dir
//		"\x00\x00"; // .zip file comment length
//	} // end of the 'file()' method
	
////////////////////////////////////////////////////////////////////////////
	

	var $file_count = 0 ;
	var $datastr_len   = 0;
	var $dirstr_len = 0;
	var $filedata = ''; //该变量只被类外部程序访问
	var $gzfilename;
	var $fp;
	var $dirstr='';
	/*
	返回文件的修改时间格式.
	只为本类内部函数调用.
	*/
    function unix2DosTime($unixtime = 0) {
    	echo '>>>>function unix2DosTIme';
        $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);

        if ($timearray['year'] < 1980) {
        	$timearray['year']    = 1980;
        	$timearray['mon']     = 1;
        	$timearray['mday']    = 1;
        	$timearray['hours']   = 0;
        	$timearray['minutes'] = 0;
        	$timearray['seconds'] = 0;
        }

        return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
               ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
    }

	/*
	初始化文件,建立文件目录,
	并返回文件的写入权限.
	*/
	function startfile($path = 'faisun.zip'){
		echo ">>>>function startfile";
		$this->gzfilename=$path;
		$mypathdir=array();
		do{
			$mypathdir[] = $path = dirname($path);
		}while($path != '.');
		@end($mypathdir);
		do{
			$path = @current($mypathdir);
//			@mkdir($path);
		}while(@prev($mypathdir));

		if($this->fp=@fopen($this->gzfilename,"w")){
			return true;
		}
		return false;
	}

	/*
	添加一个文件到 zip 压缩包中.
	*/
    function addfile($data, $name){
    	echo ">>>>function addfile";
        $name     = str_replace('\\', '/', $name);
		
		if(strrchr($name,'/')=='/') return $this->adddir($name);
		
        $dtime    = dechex($this->unix2DosTime());
        $hexdtime = '\x' . $dtime[6] . $dtime[7]
                  . '\x' . $dtime[4] . $dtime[5]
                  . '\x' . $dtime[2] . $dtime[3]
                  . '\x' . $dtime[0] . $dtime[1];
                  
        eval('$hexdtime = "' . $hexdtime . '";');

        $unc_len = strlen($data);
        $crc     = crc32($data);
        $zdata   = gzcompress($data);
        $c_len   = strlen($zdata);
        $zdata   = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
		
		//新添文件内容格式化:
        $datastr  = "\x50\x4b\x03\x04";
        $datastr .= "\x14\x00";            // ver needed to extract
        $datastr .= "\x00\x00";            // gen purpose bit flag
        $datastr .= "\x08\x00";            // compression method
        $datastr .= $hexdtime;             // last mod time and date
        $datastr .= pack('V', $crc);             // crc32
        $datastr .= pack('V', $c_len);           // compressed filesize
        $datastr .= pack('V', $unc_len);         // uncompressed filesize
        $datastr .= pack('v', strlen($name));    // length of filename
        $datastr .= pack('v', 0);                // extra field length
        $datastr .= $name;
        $datastr .= $zdata;
        $datastr .= pack('V', $crc);                 // crc32
        $datastr .= pack('V', $c_len);               // compressed filesize
        $datastr .= pack('V', $unc_len);             // uncompressed filesize

//		echo '$datastr >>> '.$datastr.'<br>';
		fwrite($this->fp,$datastr);	//写入新的文件内容
		$my_datastr_len = strlen($datastr);
		unset($datastr);
		
		//新添文件目录信息
        $dirstr  = "\x50\x4b\x01\x02";
        $dirstr .= "\x00\x00";                	// version made by
        $dirstr .= "\x14\x00";                	// version needed to extract
        $dirstr .= "\x00\x00";                	// gen purpose bit flag
        $dirstr .= "\x08\x00";                	// compression method
        $dirstr .= $hexdtime;                 	// last mod time & date
        $dirstr .= pack('V', $crc);           	// crc32
        $dirstr .= pack('V', $c_len);         	// compressed filesize
        $dirstr .= pack('V', $unc_len);       	// uncompressed filesize
        $dirstr .= pack('v', strlen($name) ); 	// length of filename
        $dirstr .= pack('v', 0 );             	// extra field length
        $dirstr .= pack('v', 0 );             	// file comment length
        $dirstr .= pack('v', 0 );             	// disk number start
        $dirstr .= pack('v', 0 );             	// internal file attributes
        $dirstr .= pack('V', 32 );            	// external file attributes - 'archive' bit set
        $dirstr .= pack('V',$this->datastr_len ); // relative offset of local header
        $dirstr .= $name;
		
		$this->dirstr .= $dirstr;	//目录信息
		
		$this -> file_count ++;
		$this -> dirstr_len += strlen($dirstr);
		$this -> datastr_len += $my_datastr_len;	
    }

	function adddir($name){ 
		echo ">>>>function adddir";
		$name = str_replace("\\", "/", $name); 
		$datastr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 
		
		$datastr .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) ); 
		$datastr .= pack("v", 0 ).$name.pack("V", 0).pack("V", 0).pack("V", 0); 

		fwrite($this->fp,$datastr);	//写入新的文件内容
		$my_datastr_len = strlen($datastr);
		unset($datastr);
		
		$dirstr = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 
		$dirstr .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) ); 
		$dirstr .= pack("v", 0 ).pack("v", 0 ).pack("v", 0 ).pack("v", 0 ); 
		$dirstr .= pack("V", 16 ).pack("V",$this->datastr_len).$name; 
		
		$this->dirstr .= $dirstr;	//目录信息

		$this -> file_count ++;
		$this -> dirstr_len += strlen($dirstr);
		$this -> datastr_len += $my_datastr_len;	
	}


	function createfile(){
		echo ">>>>function createfile";
		//压缩包结束信息,包括文件总数,目录信息读取指针位置等信息
		$endstr = "\x50\x4b\x05\x06\x00\x00\x00\x00" .
					pack('v', $this -> file_count) .
					pack('v', $this -> file_count) .
					pack('V', $this -> dirstr_len) .
					pack('V', $this -> datastr_len) .
					"\x00\x00";

		fwrite($this->fp,$this->dirstr.$endstr);
		fclose($this->fp);
	}
		
	function listfiles($dir="."){
		echo ">>>>function listfiles";
		global $faisunZIP;
		$sub_file_num = 0;

		if(is_file("$dir")){
		  if(realpath($faisunZIP ->gzfilename)!=realpath("$dir")){
			$faisunZIP -> addfile(implode('',file("$dir")),"$dir");
			return 1;
		  }
			return 0;
		}
		
		$handle=opendir("$dir");
		while ($file = readdir($handle)) {
		   if($file=="."||$file=="..")continue;
		   if(is_dir("$dir/$file")){
			 $sub_file_num += $this->listfiles("$dir/$file");
		   }
		   else {
		   	   if(realpath($this ->gzfilename)!=realpath("$dir/$file")){
			     $this -> addfile(implode('',file("$dir/$file")),"$dir/$file");

				 $sub_file_num ++;
				}
		   }
		}
		closedir($handle);
		if(!$sub_file_num) $this -> addfile("","$dir/");
		return $sub_file_num;
	}

	function num_bitunit($num){
		echo ">>>>function num_bitunit";
	  $bitunit=array(' B',' KB',' MB',' GB');
	  for($key=0;$key<count($bitunit);$key++){
		if($num>=pow(2,10*$key)-1){ //1023B 会显示为 1KB
		  $num_bitunit_str=(ceil($num/pow(2,10*$key)*100)/100)." $bitunit[$key]";
		}
	  }
	  return $num_bitunit_str;
	}	
	
	function ZipZip(){
		$zipname = "faisunzip.zip";
		
		$todir = "files/download/";
		
		$dfile = array("/test");
		
		if(is_array($dfile)){
//			$faisunZIP = new PHPzip;
//			if($faisunZIP -> startfile("$todir$zipname")){
			if($this->startfile("$todir$zipname")){
				echo "正在添加压缩文件...<br><br>";
				$filenum = 0;
				foreach($dfile as $file){
					if(is_file($file)){
						echo "文件: $file<br>";
					}else{
						echo "目录: $file<br>";
					}
					$filenum += $this->listfiles($file);
				}
				$this->createfile();
//				$faisunZIP -> createfile();
				echo "<br>压缩完成,共添加 $filenum 个文件.<br><a href='$todir$zipname'>$todir$zipname (".$this->num_bitunit(filesize("$todir$zipname")).")</a>";
			}else{
				echo "$todir$zipname 不能写入,请检查路径或权限是否正确.<br>";
			}
		}else{
			echo "没有选择的文件或目录.<br>";
		}
	}
	
	
}

?>

